from datetime import datetime, timedelta
from typing import Optional
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from passlib.context import CryptContext
from jose import JWTError, jwt
import logging

from app.models.database import User
from app.models.schemas import UserCreate, UserLogin, UserInfo, Token
from app.config import settings

logger = logging.getLogger(__name__)

# Password hashing
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

class AuthService:
    """认证服务 - 处理用户认证和授权"""
    
    def __init__(self):
        self.secret_key = settings.SECRET_KEY
        self.algorithm = "HS256"
        self.access_token_expire_minutes = settings.ACCESS_TOKEN_EXPIRE_MINUTES
        self.refresh_token_expire_days = settings.REFRESH_TOKEN_EXPIRE_DAYS
    
    def verify_password(self, plain_password: str, hashed_password: str) -> bool:
        """验证密码"""
        return pwd_context.verify(plain_password, hashed_password)
    
    def get_password_hash(self, password: str) -> str:
        """生成密码哈希"""
        return pwd_context.hash(password)
    
    def create_access_token(self, data: dict, expires_delta: Optional[timedelta] = None) -> str:
        """创建访问令牌"""
        to_encode = data.copy()
        if expires_delta:
            expire = datetime.utcnow() + expires_delta
        else:
            expire = datetime.utcnow() + timedelta(minutes=self.access_token_expire_minutes)
        
        to_encode.update({"exp": expire, "type": "access"})
        encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
        return encoded_jwt
    
    def create_refresh_token(self, data: dict) -> str:
        """创建刷新令牌"""
        to_encode = data.copy()
        expire = datetime.utcnow() + timedelta(days=self.refresh_token_expire_days)
        to_encode.update({"exp": expire, "type": "refresh"})
        encoded_jwt = jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm)
        return encoded_jwt
    
    def verify_token(self, token: str, token_type: str = "access") -> Optional[dict]:
        """验证令牌"""
        try:
            payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
            if payload.get("type") != token_type:
                return None
            return payload
        except JWTError:
            return None
    
    async def authenticate_user(self, username: str, password: str, db: AsyncSession) -> Optional[User]:
        """验证用户凭据"""
        try:
            stmt = select(User).where(User.username == username, User.is_active == True)
            result = await db.execute(stmt)
            user = result.scalar_one_or_none()
            
            if not user:
                return None
            
            if not self.verify_password(password, user.hashed_password):
                return None
            
            return user
            
        except Exception as e:
            logger.error(f"Error authenticating user {username}: {e}")
            return None
    
    async def create_user(self, user_data: UserCreate, db: AsyncSession) -> Optional[UserInfo]:
        """创建新用户"""
        try:
            # 检查用户名是否已存在
            stmt = select(User).where(User.username == user_data.username)
            result = await db.execute(stmt)
            if result.scalar_one_or_none():
                raise ValueError("Username already exists")
            
            # 检查邮箱是否已存在
            stmt = select(User).where(User.email == user_data.email)
            result = await db.execute(stmt)
            if result.scalar_one_or_none():
                raise ValueError("Email already exists")
            
            # 创建新用户
            hashed_password = self.get_password_hash(user_data.password)
            user = User(
                username=user_data.username,
                email=user_data.email,
                full_name=user_data.full_name,
                hashed_password=hashed_password,
                role="user"
            )
            
            db.add(user)
            await db.commit()
            await db.refresh(user)
            
            logger.info(f"User created: {user.username}")
            
            return self._user_to_info(user)
            
        except Exception as e:
            await db.rollback()
            logger.error(f"Failed to create user: {e}")
            raise
    
    async def get_user_by_id(self, user_id: str, db: AsyncSession) -> Optional[User]:
        """根据ID获取用户"""
        try:
            stmt = select(User).where(User.id == user_id, User.is_active == True)
            result = await db.execute(stmt)
            return result.scalar_one_or_none()
        except Exception as e:
            logger.error(f"Error getting user by ID {user_id}: {e}")
            return None
    
    async def get_user_by_username(self, username: str, db: AsyncSession) -> Optional[User]:
        """根据用户名获取用户"""
        try:
            stmt = select(User).where(User.username == username, User.is_active == True)
            result = await db.execute(stmt)
            return result.scalar_one_or_none()
        except Exception as e:
            logger.error(f"Error getting user by username {username}: {e}")
            return None
    
    async def login(self, login_data: UserLogin, db: AsyncSession) -> Optional[Token]:
        """用户登录"""
        try:
            # 验证用户凭据
            user = await self.authenticate_user(login_data.username, login_data.password, db)
            if not user:
                return None
            
            # 更新最后登录时间
            user.last_login = datetime.utcnow()
            await db.commit()
            
            # 创建令牌
            access_token = self.create_access_token(data={"sub": user.id, "username": user.username})
            refresh_token = self.create_refresh_token(data={"sub": user.id, "username": user.username})
            
            return Token(
                access_token=access_token,
                refresh_token=refresh_token,
                token_type="bearer",
                expires_in=self.access_token_expire_minutes * 60,
                user_info=self._user_to_info(user)
            )
            
        except Exception as e:
            logger.error(f"Login failed for user {login_data.username}: {e}")
            return None
    
    async def refresh_token(self, refresh_token: str, db: AsyncSession) -> Optional[Token]:
        """刷新访问令牌"""
        try:
            # 验证刷新令牌
            payload = self.verify_token(refresh_token, "refresh")
            if not payload:
                return None
            
            user_id = payload.get("sub")
            if not user_id:
                return None
            
            # 获取用户信息
            user = await self.get_user_by_id(user_id, db)
            if not user:
                return None
            
            # 创建新的访问令牌
            access_token = self.create_access_token(data={"sub": user.id, "username": user.username})
            
            return Token(
                access_token=access_token,
                refresh_token=refresh_token,  # 保持原刷新令牌
                token_type="bearer",
                expires_in=self.access_token_expire_minutes * 60,
                user_info=self._user_to_info(user)
            )
            
        except Exception as e:
            logger.error(f"Token refresh failed: {e}")
            return None
    
    def _user_to_info(self, user: User) -> UserInfo:
        """将用户模型转换为用户信息"""
        return UserInfo(
            id=user.id,
            username=user.username,
            email=user.email,
            full_name=user.full_name,
            role=user.role,
            is_active=user.is_active,
            created_at=user.created_at,
            last_login=user.last_login
        )
